package com.wdl.webserver.api;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.MediaType;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.wdl.webserver.Utils;
import com.wdl.webserver.ZuulFilters;
import com.wdl.webserver.data.AlarmType;
import com.wdl.webserver.data.ProRangeResponse;
import com.wdl.webserver.data.ProResponse;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;

@RestController
@RequestMapping("/api/pro")
@Api(value="prometheus", description="Prometheus data access")
public class PrometheusService {
    private static final Logger LOG = LoggerFactory.getLogger(PrometheusService.class);

    @Value("${zuul.routes.backend.url}")
    String datarest;

    @Value("${zuul.routes.prometheus.url}")
    String prometheus;

    @Value("${webserver.device.disconnect}")
    long deviceTimout;

    @Autowired
    public RestTemplate restTemplate;

    /**
     * Used for alarm counters report since last 8 AM.
     */
    @GetMapping(value = "/allAlarmCounters", produces = MediaType.APPLICATION_JSON)
    @ApiOperation(value = "Get all alarm counters for a specified person for all alarms given the time range in seconds.")
    public Map<String, Object> getAllAlarmCounter(
            @ApiParam("Person Id") @RequestParam("personId") long personId,
            @ApiParam("interval") @RequestParam("interval") long interval,
            HttpServletRequest request) {
        LOG.info(Utils.getLogPrefix(request) + "started");

        ZuulFilters.validatePersonId(request, personId, "/api/pro/allAlarmCounters");

        Map<String, Object> response = new HashMap<String, Object>();

        long startingTime = System.currentTimeMillis() - interval * 1000;

        long count = getAlarmCounterFromDb(personId, AlarmType.MOVE.ordinal(), startingTime);
        response.put("move", count);

        count = getAlarmCounterFromDb(personId, AlarmType.AWAY.ordinal(), startingTime);
        response.put("awayAlarm", count);

        count = getAlarmCounterFromDb(personId, AlarmType.BREATHE.ordinal(), startingTime);
        response.put("breatheAlarm", count);

        count = getAlarmCounterFromDb(personId, AlarmType.HEART.ordinal(), startingTime);
        response.put("heartAlarm", count);

        count = getAlarmCounterFromDb(personId, AlarmType.WET.ordinal(), startingTime);
        response.put("wetAlarm", count);

        LOG.info(Utils.getLogPrefix(request) + "ended.");
        return response;
    }

    private long getAlarmCounterFromDb(long personId, int alarm, long startTime) {
        if (alarm == AlarmType.MOVE.ordinal()) {
            return restTemplate.getForObject(this.datarest + "/data/movecount?personId=" + personId + "&from=" + startTime, Long.class);
        } else {
            return restTemplate.getForObject(this.datarest + "/data/alarm/search/countAlarmTypeAfter?personId=" + personId + "&alarm=" + alarm + "&from=" + startTime, Long.class);
        }
    }

    @GetMapping(value = "/statistics", produces = MediaType.APPLICATION_JSON)
    @ApiOperation(value = "Get all persons and devices statistics for the specified institution id.")
    public Map<String, Long> getStatistics(
            @ApiParam("institutionId") @RequestParam("institutionId") long institutionId,
            HttpServletRequest request) {
        LOG.info(Utils.getLogPrefix(request) + "started");

        ZuulFilters.validateInstitutionId(request, institutionId, "/api/pro/statistics");

        Map<String, Long> response = new HashMap<String, Long>();

        response.put("countallin", restTemplate.getForObject(this.datarest + "/data/person/search/countallin?institutionId=" + institutionId, Long.class));
        response.put("countnormalin", restTemplate.getForObject(this.datarest + "/data/person/search/countnormalin?institutionId=" + institutionId, Long.class));
        response.put("countawayin", restTemplate.getForObject(this.datarest + "/data/person/search/countawayin?institutionId=" + institutionId, Long.class));

        response.put("countall", restTemplate.getForObject(this.datarest + "/data/device/search/countall?institutionId=" + institutionId, Long.class));
        response.put("countinuse", restTemplate.getForObject(this.datarest + "/data/device/search/countinuse?institutionId=" + institutionId, Long.class));
        long updateTime = System.currentTimeMillis() - this.deviceTimout;
        response.put("countabnormal", restTemplate.getForObject(this.datarest + "/data/device/search/countabnormal?institutionId=" + institutionId + "&updateTime=" + updateTime, Long.class));

        LOG.info(Utils.getLogPrefix(request) + "ended.");
        return response;
    }

    /**
     * Used for heart, breathe and sleep data calculations!
     */
    @GetMapping(value = "/counterRange", produces = MediaType.APPLICATION_JSON)
    @ApiOperation(value = "Get counters for a specified person with specified label given the time range and step in seconds.")
    public Object[] getCounterListInRangeInternal(
            @ApiParam("Counter") @RequestParam("counter") String counter,
            @ApiParam("Person Id") @RequestParam("personId") long personId,
            @ApiParam("startTime") @RequestParam("startTime") long startTime,
            @ApiParam("endTime") @RequestParam("endTime") long endTime,
            @ApiParam("step") @RequestParam("step") int step,
            HttpServletRequest request) {
        ZuulFilters.validatePersonId(request, personId, "/api/pro/counterRange");

        String personJson = "{personId=\"" + personId + "\"}";
        String url = this.prometheus + "/api/v1/query_range?query=" + counter + "{personJson}&start=" + startTime + "&end=" + endTime + "&step=" + step + "&timeout=4";

        try {
            ProRangeResponse result = restTemplate.getForEntity(url, ProRangeResponse.class, personJson).getBody();
            if (result.isSuccess() && result.getCounters() != null) {
                return result.getCounters();
            }
        } catch (Exception e) {
            LOG.error("Exception Caught when send rest request to prometheus!", e);
        }

        return null;
    }

    /**
     * Used to get current heart, breathe data.
     */
    @GetMapping(value = "/realTimeData", produces = MediaType.APPLICATION_JSON)
    @ApiOperation(value = "Get the realtime hearbeat and breathe rate data for a specified person.")
    public Map<String, Long> getCounterListInRangeInternal(
            @ApiParam("Person Id") @RequestParam("personId") long personId,
            HttpServletRequest request) {
        ZuulFilters.validatePersonId(request, personId, "/api/pro/realTimeData");

        String personJson = "{personId=\"" + personId + "\"}";
        String url = this.prometheus + "/api/v1/query?query=breathe{personJson}";

        Map<String, Long> response = new HashMap<String, Long>();
        try {
            ProResponse result = restTemplate.getForEntity(url, ProResponse.class, personJson).getBody();
            if (result.isSuccess()) {
                response.put("breathe", result.getCounter());
            }

            url = this.prometheus + "/api/v1/query?query=heart{personJson}";
            result = restTemplate.getForEntity(url, ProResponse.class, personJson).getBody();
            if (result.isSuccess()) {
                response.put("heart", result.getCounter());
            }
        } catch (Exception e) {
            LOG.error("Exception Caught when send rest request to prometheus!", e);
        }

        return response;
    }
}